package com.ruoyi.common.core.utils;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;

import java.io.UnsupportedEncodingException;

public class SignUtils {

    private CacheManager cm;

    public SignUtils(CacheManager cm){
    	this.cm = cm;
    }

    /**
     * 校验参数-验证是否传入值
     * 有个很重要的一点，就是对此请求进行时间验证，如果大于10分钟表示此链接已经超时，防止别人来到这个链接去请求。这个就是防止盗链。
     * @return
     */
    public static boolean verifyParams(String appKey, String timestamp, String nonce, String sign) {
        //应用id
        if (StringUtils.isEmpty(appKey)) {
            System.out.println("appKey is null");
            return false;
        }

        //时间戳,增加链接的有效时间,超过阈值,即失效
        if (StringUtils.isEmpty(timestamp)) {
            System.out.println("timestamp is null");
            return false;
        }

        //调用方传递的签名
        if (StringUtils.isEmpty(sign)) {
            System.out.println("sign is null");
            return false;
        }

        // 临时流水号/随机串 ,至少为10位 ，有效期内防重复提交
        if (StringUtils.isEmpty(nonce)) {
            System.out.println("nonce is null");
            return false;
        }

        //毫秒
        long diff = System.currentTimeMillis() - Long.parseLong(timestamp);
        //大于10分钟
        if (diff > 1000 * 60 * 10) {
            System.out.println("timestamp is timeout");
            return false;
        }

        return true;
    }

    /**
     * 将所有请求参数与应用密钥appSecret进行排序加密后生成签名(signature) 然后与 调用方法传递的签名(signature)进行比较
     *
     * @param appSecret 应用id对应的应用密钥
     * @return 签名比较结构 true为签名正确
     */
    public static boolean verifySignature(String appKey, Long timestamp, Long nonce, String sign, String appSecret) {
        //log.info("调用方传过来的Sign:{}", sign);
        if (StringUtils.isEmpty(appSecret)) {
            return false;
        }
        //将调用方的请求参数 与 应用密钥 按签名规则处理后生成的签名
        String signature = generateSignature(appKey, timestamp, nonce, sign, appSecret);
        //log.info("后端生成的Sign:{}", signature);

        //比较调用方传的签名 与 后台生成的签名
        return StringUtils.isNotEmpty(signature) && StringUtils.equals(sign, signature);
    }

    /**
     * 所有的参数与应用密钥appSecret 进行排序加密后生成签名
     *
     * @param appSecret 应用id对应的应用密钥
     * @return 生成接口签名
     */
    public static String generateSignature(String appKey, Long timestamp, Long nonce, String sign, String appSecret) {
        StringBuilder plainText = new StringBuilder();
        plainText.append(appSecret).append(timestamp).append(nonce);
        //将digest 转换成UTF-8 的 byte[] 后 使用MD5算法加密，最后将生成的md5字符串转换成大写
        try {
            return Tool.shaEncode(plainText.toString());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        return null;
    }
    
    /**
     * 上面的流程中，会有个额外的安全处理，防止盗链，我们可以让链接有失效时间
     * 而利用nonce参数，可以防止重复提交,在签名验证成功后，判断是否重复提交,原理就是结合redis，判断是否已经提交过
     *
     * @param timeStamp 13位时间戳
     * @param nonce     临时流水号/随机串 ,至少为10位 ，有效期内防重复提交
     * @param signature 接口签名
     * @return 是否重复请求
     */
    public boolean isReplayAttack(String appKey, String timeStamp, String nonce, String signature) {
        StringBuilder ehcacheKey = new StringBuilder();
        ehcacheKey.append("IS_REPLAY_ATTACK").append(":")
                .append(Constant.APP_KEY).append(":").append(appKey)
                .append(Constant.TIME_STAMP).append(":").append(timeStamp)
                .append(Constant.NONCE).append(":").append(nonce)
                .append(Constant.SIGN).append(":").append(signature);
        Cache<String, Object> cache = cm.getCache("isReplayAttack");//通过cm可以生成指定名称(cachename)的Cache对象
		//对于以后查询的种类会更多因此这个参数需要采用传值  ex:articleCache，userCache，imageCache
		String sign = (String) cache.get(ehcacheKey.toString());//从cache(key)中取回元素
		
		if (sign != null && StringUtils.equals(signature, sign)) {
			return false;
		}else {
			cache.put(ehcacheKey.toString(), signature);
	        return true;
		}
    }


}